package cn.edu.neu.lab603.cloudeval.entity

import cn.edu.neu.lab603.cloudeval.entity.VirtualMachine.NetworkPacket
import cn.edu.neu.lab603.cloudeval.entity.model.ResModel

import scala.collection.mutable

/** 为用户设计的 Cloudlet DSL .
  * 提供了几个方面的方法对任务进行扩展：
  *
  * 1. 可以通过 at 语法来定制虚拟机动作，例如：
  * <code>
  * at(10).exec { vm =>
  *     vm.net.send(22, 100)
  * }
  * </code>
  * 即在 10 的时钟周期里，执行 exec 回调的内容。at 的参数指的是当前 cloudlet 获得的可用时钟，也就是说，是相对于任务的时钟而言，而非全局。
  *
  * 2. 通过 received 注册监听事件，以处理网络事件。
  *
  * @author yfwz100
  */
class UserCloudTask(start: Int,
                    length: Int,
                    resMap: Map[String, ResModel],
                    dataCenter: DataCenter) extends CloudTask(start, length, resMap, dataCenter) {

  private val atWords = new mutable.HashMap[Int, VirtualMachine => Unit]
  private val netReceivers = new mutable.ListBuffer[NetworkPacket => Any]

  sealed class AtWord protected[entity](clock: Int) {
    def exec(proc: VirtualMachine => Unit): Unit = {
      atWords(clock) = proc
    }
  }

  /** 在当前的第几个可用周期里执行。
    *
    * @param clock 时钟周期
    * @return At 语义
    */
  final def at(clock: Int): AtWord = new AtWord(clock)

  /** 在下一个可用周期里执行。
    *
    * @return At 语义
    */
  final def atNextTick: AtWord = new AtWord(getCursor + 1)

  /** 当接收到网络事件后执行的流程。
    *
    * @param callback 执行的流程
    */
  final def received(callback: NetworkPacket => Any): Unit = netReceivers += callback

  final override def receivedNetPacket(data: NetworkPacket): Unit = {
    netReceivers.foreach(_ (data))
  }

  override def update(clock: Int): Unit = {
    super.update(clock)

    if (getStatus == CloudTask.Status.RUNNING) {
      if (atWords contains getCursor) {
        atWords(getCursor).apply(getExecutionContext)
      }
    }
  }

}

